# -*- encoding: latin-1 -*-
##############################################################################
#
# Copyright (c) 2009 Àngel Àlvarez - NaN  (http://www.nan-tic.com) All Rights Reserved.
#
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

import datetime

from osv import osv
from osv import fields
from tools.translate import _
import netsvc
import time
import pooler

logger = netsvc.Logger()

class mrp_procurement(osv.osv):
    _inherit = 'mrp.procurement'
    
    def _procure_orderpoint_confirm(self, cr, uid, automatic=False,use_new_cursor=False, context=None, user_id=False):
        
        seqObj =  self.pool.get('ir.sequence')
#        seqIDs = seqObj.search(cr,uid,[('code','=','mrp.production')])    
#        seqObj.write(cr,uid,seqIDs[0],{'active':False})
        cr2 = pooler.get_db(use_new_cursor).cursor()
        cr2.execute('UPDATE ir_sequence SET active=%s WHERE code=%s',(False,'mrp.production'))
        cr2.commit()
        try:
            returnVal = super(mrp_procurement, self)._procure_orderpoint_confirm( cr, uid, automatic,use_new_cursor, context, user_id)
        finally:            
#        seqObj.write(cr,uid,seqIDs[0],{'active':True})
            cr2.execute('UPDATE ir_sequence SET active=%s WHERE code=%s',(True,'mrp.production'))
            cr2.commit()
            cr2.close()
        prod_obj = self.pool.get('mrp.production')
        orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
        uom_obj = self.pool.get('product.uom')

#        to_split_production_orders_ids = prod_obj.search(cr, uid, [('state','in',('confirmed',)),('is_from_mps','=',True)])
        mrp_generated_production_orders_ids = prod_obj.search(cr, uid, [('state','in',('confirmed',)),('name','=','/'),('origin','ilike','op/%')])
        logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'mrp_generated_production_orders_ids:%s' %(mrp_generated_production_orders_ids)) 
#        to_split_production_orders_objects = prod_obj.browse(self, cr, uid,to_split_production_orders_ids)
        for orderID in mrp_generated_production_orders_ids:
            order = prod_obj.browse(cr, uid,orderID)
            productID = order.product_id.id
            orderPointsIDs = orderpoint_obj.search(cr, uid, [('product_id','=',productID)],)
            orderPointsObj =orderpoint_obj.browse(cr, uid, orderPointsIDs[0],)
            batchSize = uom_obj._compute_qty(cr, uid, orderPointsObj.product_uom.id, orderPointsObj.qty_multiple, order.product_uom.id) 
            #add the name
            prod_obj.write(cr, uid, order.id,{'name':seqObj.get(cr,uid,'mrp.production')})
            if  order.product_qty > batchSize and order.is_from_mps: 
                logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'spliting production order:%s with batch size %s' %(order.id,batchSize)) 

                prod_obj._split(cr, uid, order.id, batchSize, context)
            
        logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'done and returning')   
        cr.commit() 
        return {}   
mrp_procurement()
 
class mrp_production(osv.osv):
    _inherit = 'mrp.production'

    def _change_prod_qty(self, cr, uid, id ,quantity, context,newProdName=False, oldProdName=False):

        prod_obj = self.pool.get('mrp.production')
        procurement_obj = self.pool.get('mrp.procurement')
        prod = prod_obj.browse(cr, uid, id , context=context)
        prod_obj.write(cr, uid, prod.id, {'product_qty' : quantity })
        prod_obj.action_compute(cr, uid, [prod.id])

        move_lines_obj = self.pool.get('stock.move')
        for move in prod.move_lines:
            bom_point = prod.bom_id
            bom_id = prod.bom_id.id
            if not bom_point:
                bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, prod.product_id.id, prod.product_uom.id)
                if not bom_id:
                    raise osv.except_osv(_('Error'), _("Couldn't find bill of material for product"))
                self.write(cr, uid, [prod.id], {'bom_id': bom_id})
                bom_point = self.pool.get('mrp.bom').browse(cr, uid, [bom_id])[0]

            if not bom_id:
                raise osv.except_osv(_('Error'), _("Couldn't find bill of material for product"))

            factor = prod.product_qty * prod.product_uom.factor / bom_point.product_uom.factor
            res = self.pool.get('mrp.bom')._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, [])
            for r in res[0]:
                if r['product_id']== move.product_id.id:
                    move_lines_obj.write(cr, uid,move.id, {'product_qty' :  r['product_qty']})
                    
                # Added By Amgad Alaa 19-8-2011
                #Cause: also change reservation picking quantities     
                for picking_moves in prod.picking_id.move_lines:
                    if r['product_id']== picking_moves.product_id.id: 
                        #added by Amgad Alaa 169-2011
                        #Cause: change parent procurement values
                        if newProdName:
                            parentProcID = procurement_obj.search(cr, uid, [('move_id', '=', picking_moves.id)] )
                            parentProcObj = procurement_obj.browse(cr, uid, parentProcID)[0]
                            oldProcName = parentProcObj.name
                            oldOriginName = parentProcObj.origin
                            #modified by Amgad Alaa  27-9-2011
                            #Cause: disable production seq cause name to go wrong
                            #newProcName = oldProcName.replace(oldProdName,newProdName)
                            #newOriginName = oldOriginName.replace(oldProdName,newProdName)
                            newProcName = oldProcName.replace(':/_',':'+newProdName+'_')
                            newOriginName = oldOriginName.replace(':/',':'+newProdName)
                            ###############################
                            procurement_obj.write(cr,uid,[parentProcObj.id] , {'product_qty': r['product_qty'] , 'name':newProcName,'origin':newOriginName})
                            reservationMoveNewName = 'PROD:'+newProdName
                            move_lines_obj.write(cr, uid, [picking_moves.id], {'product_qty' :  r['product_qty'],'name':reservationMoveNewName}) 
                            
                            #added by Amgad Alaa 28-9-2011
                            #Cause: disable production seq cause name to go wrong 
                            rmProcMoveObj = picking_moves.move_dest_id
                            rmProcMoveNameOld =  rmProcMoveObj.name
                            rmProcMoveNameNew = rmProcMoveNameOld.replace(':/',':'+newProdName)
                            move_lines_obj.write(cr, uid, [rmProcMoveObj.id], {'name':rmProcMoveNameNew}) 
                            ###############################
                        else:
                            move_lines_obj.write(cr, uid, [picking_moves.id], {'product_qty' :  r['product_qty']})
#                       ###############################################
                ################################
                    

        product_lines_obj = self.pool.get('mrp.production.product.line')

        for m in prod.move_created_ids:
            #added by Amgad Alaa 28-9-2011
            #Cause: disable production seq cause name to go wrong 
            if newProdName:
                fgProcMoveNameOld =  m.name
                fgProcMoveNameNew = fgProcMoveNameOld.replace(':/',':'+newProdName)
                move_lines_obj.write(cr, uid,m.id, {'product_qty' : quantity,'name':fgProcMoveNameNew})
            #################################
            else:    
                move_lines_obj.write(cr, uid,m.id, {'product_qty' : quantity})

        return {}

    def _update_picking( self,cr,uid,id,try_assign=False, context=None ):
        if context == None:
            context={}

        prod = self.browse(cr,uid,id,context=context)

        cancel_moves =  [x.id for x in prod.picking_id.move_lines]
        lines = []
        for move_line in  prod.move_lines:
            new_mome_id = self.pool.get('stock.move').copy(cr,uid, move_line.id, {'location_dest_id':prod.location_src_id.id, 'picking_id':prod.picking_id.id,'state':'confirmed'}, context = context)
            lines.append(new_mome_id)
        self.pool.get('stock.picking').browse(cr,uid,prod.picking_id.id,context=context)

        self.pool.get('stock.picking').write( cr, uid, prod.picking_id.id, {'move_lines':[(6,0,lines)]}, context=context)
        self.pool.get('stock.move').action_cancel(cr,uid, cancel_moves,context)
        if try_assign:
            self.pool.get('stock.picking')._try_assign(cr, uid, production.picking_id, context)

    
    #Added by Amgad 5-1-2011
    #cause: over-ridding production cancel function, to cancel chainned location also
    def action_cancel(self, cr, uid, ids):
        move_lines_obj = self.pool.get('stock.move')
        for production in self.browse(cr, uid, ids):
            if production.move_created_ids:
                self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in production.move_created_ids])
                for x in production.move_created_ids:
                    cr.execute('select child_id from stock_move_history_ids where parent_id = %s' %(x.id) )  
                    move_lines_obj.action_cancel(cr,uid, [y for y, in cr.fetchall()])
                    
            self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in production.move_lines])
        self.write(cr, uid, ids, {'state':'cancel'}) #,'move_lines':[(6,0,[])]})
        return True

    def _split(self, cr, uid, id, quantity, context,startUpMOList=[],isThereNextMO=False):
        """
        Sets the quantity to produce for production with id 'id' to 'quantity' and
        creates a new production order with the deference between current amount and
        the new quantity.
        """
        production = self.browse(cr, uid, id, context)
        procurement_obj = self.pool.get('mrp.procurement')
        move_lines_obj = self.pool.get('stock.move')
        workflow = netsvc.LocalService("workflow")
        if production.state not in ('confirmed'):
            raise osv.except_osv(_('Error !'), _('Production order "%s" is not in "Waiting Goods" state.') % production.name)

#commented cause we want to produce in multiples, so in this case produce the batch size QTY
#        if quantity >= production.product_qty:
#            raise osv.except_osv(_('Error !'), _('Quantity must be greater than production quantity in order "%s" (%s / %s)') % (production.name, quantity, production.product_qty))


        #added By Amgad 5-1-2011
        #cause: create new production orders each one with the specified quantity
        # then cancel existing production
        #new_production_ids=[]
        excessQTY = production.product_qty % quantity
        #Added by Amgad Alaa 18-8-2011
        #Cause: always production order will be in batch size
        # so excessQTY  will be produced in a new producion order with batch size
        if excessQTY > 0:
             noOfFullQTYBatches = ((production.product_qty - excessQTY) / quantity) +1
        else:
             noOfFullQTYBatches = production.product_qty  / quantity
             
        logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'number of batches:%s, QTY:%s' %(noOfFullQTYBatches, quantity)) 
        #######################################
        
        # Create new production, but ensure product_lines is kept empty.
        count = 0
#        mpsExpectedFinishStr = production.date_finnished
#        mpsExpectedFinishDate = time.strptime(mpsExpectedFinishStr,'%Y-%m-%d %H:%M:%S')
#        logger.notifyChannel('_split', netsvc.LOG_WARNING, 'mpsExpectedFinishDate:%s' %(mpsExpectedFinishDate)) 
        productionState = production.state
        
        #Added by Amgad Alaa 10-9-2011
        #Cause: fixing manufacture orders splitting naming
        ordersSeqs ={}
        for x in range(1,noOfFullQTYBatches):
            ordersSeqs.__setitem__(x, self.pool.get('ir.sequence').get(cr, uid, 'mrp.production'))
         ######################################
        while count < noOfFullQTYBatches:
        
        ############# not implemented ##################
        #distinguish between two cases:
        #-from mps: start from the end that calculate mps_base_date then 
        #    get date planned to be the next mps_base_date(start from end to beginning)
        #if not from MPS: start from the date_planned then get finish date to be next date_planned (start from beginning to end)
        ################################################
        
        
        # currently i will handle the case of from mps only cause when creating
        # production order manually i will force him to choose one batch only every time
        #but in case of confirmed
            #create new orders
            #and modify qty in the current production 
        
        #in case or ready
            # think about force assigment for new orders
            #and modify qty in the current production order plus recreate reservation picking
            #make sure that work orders are right
            if count == 0:
                if productionState == 'confirmed':
                    logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'first round,confirmed, changing qty') 
                    logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'used mps_base_date:%s'%(production.date_finnished)) 
                    if not ordersSeqs.get(noOfFullQTYBatches -1,False):
                        self.write(cr, uid,production.id, {'date_start' :  False,'mps_base_date':production.date_finnished})
                        self._change_prod_qty( cr, uid, id ,quantity, context)
                    else:
                        self.write(cr, uid,production.id, {'date_start' :  False,'mps_base_date':production.date_finnished,'name':ordersSeqs[noOfFullQTYBatches -1]})
                        self._change_prod_qty( cr, uid, id ,quantity, context, newProdName = ordersSeqs[noOfFullQTYBatches -1], oldProdName = production.name)
                    ordersSeqs.__setitem__(0, production.name)
#                else:
#                    if productionState == 'ready':
#                        logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'first round,ready, changing qty') 
#                        logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'used mps_base_date:%s'%(production.date_finnished)) 
#                        self.write(cr, uid,production.id, {'product_qty' :  quantity,'date_start' :  False,'mps_base_date':production.date_finnished})
#                        # but the old moves will still exist we need to cancel them 
#                        move_lines_obj.action_cancel(cr,uid,[x.id for x in  production.move_lines],context)
#                        move_lines_obj.action_cancel(cr,uid, [x.id for x in  production.move_created_ids] ,context)
#                        self.action_compute(cr, uid,  [production.id])
#                        # action confirm for recreate reservation picking
#                        self.action_confirm( cr, uid, [production.id])
#                        self.force_production( cr, uid, [production.id])
                # i think we have to browse again cause refrenece we have might have not been updated
                refreshedProductionObj = self.browse(cr, uid, production.id, context)
#                mpsExpectedFinishStr = production.date_finnished
#                mpsExpectedFinishDate = time.strptime(mpsExpectedFinishStr,'%Y-%m-%d %H:%M:%S')
                nextMPSDueDateStr = refreshedProductionObj.date_planned
                nextMPSDueDate = time.strptime(nextMPSDueDateStr,'%Y-%m-%d %H:%M:%S')
                logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'refreshedProductionObj id:%s, nextMPSDueDate: %s'%(refreshedProductionObj.id,nextMPSDueDateStr))
                    
            else:
                #get the name of first order, and use it in last splitted order
                if productionState == 'confirmed':
                    logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'round %s,confirmed, copying'%(count))
                    new_production_id = self.copy(cr, uid, id, {
                    'name':ordersSeqs[noOfFullQTYBatches -1 - count],
                    'product_lines': [],
                    'move_prod_id': False,
                    'product_qty':  quantity,
                    'state':'draft',
                    'picking_id':'',
                    'workcenter_lines':[],
                    'date_start':False,
                    'mps_base_date': time.strftime('%Y-%m-%d %H:%M:%S',nextMPSDueDate),
                     }, context)

                    workflow.trg_validate(uid, 'mrp.production', new_production_id, 'button_confirm', cr) 
#                if productionState == 'ready':
#                    logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'round %s,ready, copying'%(count))
#                    new_production_id = self.copy(cr, uid, id, {
#                    'product_lines': [],
#                    'move_prod_id': False,
#                    'product_qty':  quantity,
#                    'state':'draft',
#                    'picking_id':'',
#                    'workcenter_lines':[],
#                    'date_start':False,
#                    'mps_base_date': time.strftime('%Y-%m-%d %H:%M:%S',nextMPSDueDate),
#                     }, context)
#                    workflow.trg_validate(uid, 'mrp.production', new_production_id, 'button_confirm', cr) 
#                    self.force_production( cr, uid, [production.id])
                #mps_base_dateis set to production.date_finnished
                #cause it is the original base date it helps, so backword computation 
                #work correctly
                newProductionObj = self.browse(cr, uid, new_production_id, context)
                nextMPSDueDateStr = newProductionObj.date_planned
                nextMPSDueDate = time.strptime(nextMPSDueDateStr,'%Y-%m-%d %H:%M:%S')
                logger.notifyChannel('split_procure_orderpoint_confirm', netsvc.LOG_WARNING, 'new production id:%s, nextMPSDueDate: %s'%(newProductionObj.id,nextMPSDueDateStr))
                #added By Amgad Alaa 28-9-2011
                #Cause: mark created procurements from MO as handled
                for picking_moves in newProductionObj.picking_id.move_lines:
                    parentProcID = procurement_obj.search(cr, uid, [('move_id', '=', picking_moves.id)] )
                    procurement_obj.write(cr,uid,parentProcID , {'mps_handling_done': True})
                ########################
#                new_production_ids.append(new_production_id)
            count =count+1
            
        #Commeneted by Amgad Alaa 18-8-2011
        #Cause: always production order will be in batch size
#        if excessQTY > 0:     
#            new_production_id = self.copy(cr, uid, id, {
#                'product_lines': [],
#                'move_prod_id': False,
#                'product_qty':  excessQTY,
#                'state':'draft',
#                'picking_id':'',
#                'workcenter_lines':[],
#                'date_start':False,
#                'date_planned':production.date_planned,
#                'mps_base_date': time.strftime('%Y-%m-%d %H:%M:%S',mpsExpectedFinishDate),
#    #            'product_qty': production.product_qty - quantity,
#            }, context)
#            new_production_ids.append(new_production_id)

            
#        self.write(cr, uid, production.id, {
#            'product_qty': quantity,
#            'product_lines': [],
#        }, context)

        # commented by Amgad Alaa 19-8-2011
        #Cause: always production order will be in batch size
        #cancel picking  related to production order
#        stock_picking_obj = self.pool.get('stock.picking')
#        prod_material_picking = production.picking_id.id
##        canCancelPicking= stock_picking_obj.test_cancel(cr, uid,[prod_material_picking])
##        if canCancelPicking:
#        stock_picking_obj.action_cancel(cr, uid,[prod_material_picking])
#        #cancel production order
#        self.action_cancel(cr, uid, [id])
#        
#        #removed by Amgad Alaa 24-4-2011
#        #cause: thw workflow.trg_validate should handle the material calculations
#        #calculate needed material for new production orders
#        #self.action_compute(cr, uid, new_production_ids)
#        #################################
#        
##        self.action_compute(cr, uid, [ new_production_id])
##        self._change_prod_qty( cr, uid, production.id ,quantity, context)
#        workflow = netsvc.LocalService("workflow")
#        for newProdID in new_production_ids:
#             workflow.trg_validate(uid, 'mrp.production', newProdID, 'button_confirm', cr)           
###########################################                    
#        return new_production_ids
#        return [id, new_production_id]

#to solve the problem of MO naming in case many MOs are auto splitted after MRP run
#notice that there may be MOs will not be splitted as they are equal batch size but has Name greater than
#another MO that need to be splitted
#in _procure_orderpoint_confirm method:
#we can disable the mrp.production sequence before calling the super method, then after the super we get the ids from sequence 
#and assign them to MOs according the need, assign them split then assign the next MO
#to make sure that we select the MOs that got no Name due to diabling sequence we can search name = / and origin contains OP /
#but watch out that not all generated MOs from MRP are from MPS
#make sure to enable the sequence again

mrp_production()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

